// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

#include "stdafx.h"
#include "OrientationGizmo.h"
#include "GizmoManager.h"
#include "Color.h"

OrientationGizmo::OrientationGizmo(GizmoManager* manager)
: Gizmo(manager)
{
}


gx::Vec3 OrientationGizmo::computeRotAxis(double dx, double dy, 
                                          const gx::Mat4& parentInverse) const
{
    gx::Mat4 gizmoFrame = m_iniGizmoFrame;

    gx::Vec3 result = gx::zero;
    for(int d = Dims::X; d <= Dims::Z; ++d)
    {
        if(!isDimSelected(Dim(d)))
        {
            continue;
        }

        gx::Vec3 axisWorld = gizmoFrame.get_col(d);
        gx::Vec3 axisLocal = parentInverse * gx::Dir(axisWorld);
        
        double delta = findPreferredScreenAxis(axisWorld) ? dx : dy;
        result += axisLocal * delta;
    }
    return result;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -  

void OrientationGizmo::onNodeDrag(const ScreenPoint & dragPoint, 
                                  gx::Mat4 & localTransform,
                                  gx::Mat4 & gizmoTransform,
                                  NodeEvent::Property& prop)
{
    prop = NodeEvent::NodeOrientation;

    Transformable* item = m_manager->getDraggedItem();

    CSize delta = dragPoint - m_clickPoint;
    int w, h;
    m_manager->getView()->getDims(w, h);
    
    bool lock = false;

    double dx = TWO_PI * double(delta.cx) / w;
    double dy =  lock ? dx : TWO_PI * double(delta.cy) / h;

    // TODO use getGizmoMatrix() here if you plan the gizmo pivot or frame to
    // change in the middle of a drag cycle
    gx::Mat4 gizmoFrame = m_iniGizmoFrame;

    gx::Mat4 parentInverse(gx::eye);
    if(Node* node = dynamic_cast<Node*>(item))
    {
        gx::Mat4 parentTransform = node->getParentGlobalTransform();
        parentInverse = gx::Mat4(parentTransform).invert();
    }

    gx::Vec3 totalAxisLocal = computeRotAxis(dx, dy, parentInverse);
    gx::Mat3 Rlocal = gx::Mat3().make_rot(gx::AxisAngle(totalAxisLocal));

    gx::Vec3 pivotWorld = gizmoFrame.translation;
    gx::Vec3 pivotLocal = parentInverse * gx::Pnt(pivotWorld);
    localTransform = gx::Mat4().make_pivot_rot(pivotLocal, Rlocal);

    gizmoTransform = gx::Mat4().make_pivot_rot(pivotWorld, Rlocal);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -  

void OrientationGizmo::drawGizmoEnds(View3d * view, 
                                     const Color colors[3], double size) const
{
    glTranslated(0., 0., 1. - size * .5);
    
    setColorOrPickCode(view, colors[Dims::Z], Dims::Z);
    glutSolidSphere(size * .5, 10, 10);

    glTranslated(0., 1. - size * .5, -(1. - size * .5));
    
    setColorOrPickCode(view, colors[Dims::Y], Dims::Y);
    glutSolidSphere(size * .5, 10, 10);

    glTranslated(1. - size * .5, -(1. - size * .5), 0.);

    setColorOrPickCode(view, colors[Dims::X], Dims::X);
    glutSolidSphere(size * .5, 10, 10);

    glTranslated(-(1. - size * .5), 0., 0.);
}


